/*
** Copyright (C) 2010 Calin Radoni
**
** This file is part of CHScanner.
**
** CHScanner is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** CHScanner is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with CHScanner. If not, see <http://www.gnu.org/licenses/>.
*/

#include "StdAfx.h"
#include ".\scanmethodack.h"

//--------------------------------------------------------------------

ScanMethodAck::ScanMethodAck(void) : ScanMethod()
{
	myType = __wmtAck;
}

ScanMethodAck::~ScanMethodAck(void)
{
}

//--------------------------------------------------------------------

int ScanMethodAck::InternalInit(void)
{
	int res;

	res = BuildPortList(true);
	if(res!=MSR_ERR_OK)
		return res;
	
	FillRandomBuffer();
	
	if(workPhase==__wptDiscovery) return MSR_ERR_OK;
	if(workPhase==__wptScan)      return MSR_ERR_OK;

	return MSR_ERR_PHASE;
}

//--------------------------------------------------------------------

int ScanMethodAck::InternalExecute(void)
{
	ScanPacket* pack;

	if(addrList==NULL) return MSR_ERR_PARAMETER;

	ScanPair sp;
	while(GetNextPair(&sp, __sptIpAndPort)){

		CheckResponses();
		CheckDeaths();

		if(sp.addr!=NULL){
			if(!sp.addr->addrIP.IsEmpty()){
				if(workPhase==__wptDiscovery && sp.addr->alive){
					// it's the discovery phase and a previous discovery method has found it already
					// so skip this one
				}
				else{
					if(config->xMinDelay>0){
						if(lastScanTime!=0){
							waitDone = false;
							while(!waitDone){
								CheckResponses();
								SleepEx(1, TRUE);
								scanTime = GetTickCount();
								scanTime = ComputeDeltaTime(lastScanTime, scanTime);
								if(scanTime>=config->xMinDelay)
									waitDone = true;
							}
						}
						lastScanTime = GetTickCount();
					}

					pack = FindFirstUnusedPacket();
					while(pack==NULL){
						SleepEx(waitTime, TRUE);
						CheckResponses();
						CheckDeaths();
						pack = FindFirstUnusedPacket();
					}

					if(pack!=NULL){
						pack->Initialize(true);
						pack->sTime       = GetTickCount();
						pack->addr        = sp.addr->addrIP.GetD();
						pack->port        = sp.port;
						pack->methodType  = myType;
						pack->fullAddress = sp.addr;
						pack->machineInfo = FindMachineByIp(pack->addr);

						if(!SendAck(sp.addr, sp.port))
							pack->Initialize(false);

						if((packSent % notifTcp) == 0 && packSent>0){
							if(hWnd!=NULL)
								PostMessage(hWnd, CHS_MSG_PACKETS_SENT, 0, (LPARAM)packSent);
						}
					}
				}
			}
		}
	}

	// wait until the end
	while(PacketsUsed()>0){
		CheckResponses();
		CheckDeaths();
		SleepEx(1, TRUE);
	}

	return MSR_ERR_OK;
}

//--------------------------------------------------------------------

void ScanMethodAck::ComputeRandomValues(void)
{
	rndTcpSeq = GetRandomValue();
	rndTcpAck = GetRandomValue();
}

bool ScanMethodAck::SendAck(MFullAddress* addrIn, WORD portIn)
{
	BYTE srcMac[6];
	bool res;

	if(addrIn==NULL){
		// nothing to send
		return false;
	}

	ComputeRandomValues();

	res = packetBuilderEx->BuildPackTcpFlat(
		winPcap->GetLocalIP(),
		addrIn->addrIP.GetD(),
		portIn,
		rndTcpSeq,
		rndTcpAck,
		TH_ACK);
	if(!res) return false;

	winPcap->FillSrcMAC(&srcMac[0]);
	res = packetBuilderEx->AddEthernetHeader(srcMac, addrIn->addrMAC.addr, 0x0800);
	if(!res) return false;

	return SendPacket(packetBuilderEx->GetPacket(), (int)packetBuilderEx->GetPacketSize());
}

//--------------------------------------------------------------------

void ScanMethodAck::ProcessTcp(BYTE* dataIn, int sizeIn)
{
	IPHeaderPG   *iph;
	TCPHeaderPG  *tcph;
	int           dataOffset;
	in_addr       inAddr;
	ScanPacket   *pack;
	MFullAddress *addr;
	MMachineInfo *machineInfo;

	iph = (IPHeaderPG*)dataIn;
	dataOffset  = (int)(iph->HLV & 0x0F);
	dataOffset *= 4;
	if(sizeIn<(dataOffset + 20)){
		// incomplete packet
		return;
	}

	tcph = (TCPHeaderPG*)&dataIn[dataOffset];

	if(tcph->Flags!=TH_RST){
		// this is not what I am expecting, skip
		return;
	}

	// host is alive

	inAddr.S_un.S_un_b.s_b1 = iph->Source[0];
	inAddr.S_un.S_un_b.s_b2 = iph->Source[1];
	inAddr.S_un.S_un_b.s_b3 = iph->Source[2];
	inAddr.S_un.S_un_b.s_b4 = iph->Source[3];

	pack = FindPacket(inAddr.S_un.S_addr, ntohs(tcph->SourcePort), myType);
	if(pack==NULL) return;

	if(workPhase==__wptDiscovery){
		addr = pack->fullAddress;
		if(addr!=NULL)
			addr->alive = true;
	}
	else{
		machineInfo = pack->machineInfo;
		if(machineInfo!=NULL)
			machineInfo->address.alive = true;
	}

	pack->Initialize(false);
}

//---------------------------------------------------------------------------
